#pragma once

#include <compl/candy.h>

/* 未测试, 由于arm32浮点寄存器极其混乱，这里不考虑保存arm32浮点 
	https://developer.arm.com/documentation/dui0041/c/ARM-Procedure-Call-Standard/
*/

// #define PROMPT_PREFETCH_ENABLE

#define SETJMP_NORMAL_RESUME

#if defined (RESUME_FPU)
	/* 使用慢速resume */
	#ifndef SETJMP_NORMAL_RESUME
		#define SETJMP_NORMAL_RESUME
	#endif
#endif



#define BUF_FPU
#define SAVE_FPU(a, b, p)
#define LOAD_FPU(a, b, p)

typedef struct resume_fpuctx{
	__BUF_FPU
} resume_fpuctx_t;



typedef struct cresume_buf{
	word_t sp;	// sp
	word_t lr;	// 返回地址
	word_t r4; 	// v1
	word_t r5;	// v2
	word_t r6;	// v3
	word_t r7;	// v4
	word_t r8;	// v5
	word_t r9;	// v6/sb
	word_t r10;	// v7/sl
	word_t r11;	// v8/fp
} cresume_ctx_t;

#define STCTX(reg)		"stmia	"#reg"!, {sp, lr, v1-v8}		\n"
#define LDCTX(reg)		"ldmia	"#reg"!, {sp, lr, v1-v8}		\n"

word_t __ssetjmp(cresume_ctx_t *buf) asm ("_ssetjmp");

asm ("_ssetjmp:					\n"\
	STCTX(r0)					\n\
	"mov	r0, #0				\n\
	bx	lr\n");

/* 直接return 即可，因为x0 一定不为0 */
/* 跳跃后所有本地现场失效 */
/* 使用x0作为判断, assert buf != NULL */
#define __slongjmp(buf, val)			\
asm ("						\n\
	mov	r0, %0				\n\
	/* mov	r1, %1 */			\n\
	LDCTX(r0)				\n\
	"					\
	"/* mov	r0, r1 */			\n\
	bx	lr\n"				\
	:					\
	: "r"(buf), "r"(val)			\
	: "memory");				\
UNREACHABLE()


word_t __ctxsw(cresume_ctx_t *buf, cresume_ctx_t *next_buf) asm ("_ctxsw");
asm ("_ctxsw:						\n"\
	STCTX(r0)					\
	LDCTX(r0)					\
	"ret\n");

/* 恢复r18 */
// asm ("_ctxsw:					\n\
// 	mov	x10, sp /* sp to x10 */			\n\
// 	stp	x18, x19, [x0], #16			\n\
// 	stp	x20, x21, [x0], #16			\n\
// 	stp	x22, x23, [x0], #16			\n\
// 	stp	x24, x25, [x0], #16			\n\
// 	stp	x26, x27, [x0], #16			\n\
// 	stp	x28, x29, [x0], #16 /* x28 fp */	\n\
// 	stp	x30, x10, [x0], #16 /* lr and sp */	\n\
// 	"						\
// 	SAVE_FPU(x11, x12, x0)				\
// 	"						\
// 	ldp	x18, x19, [x1], #16			\n\
// 	ldp	x20, x21, [x1], #16			\n\
// 	ldp	x22, x23, [x1], #16			\n\
// 	ldp	x24, x25, [x1], #16			\n\
// 	ldp	x26, x27, [x1], #16			\n\
// 	ldp	x28, x29, [x1], #16 /* x28 and fp */	\n\
// 	ldp	x30, x10, [x1], #16 /* lr and sp */	\n\
// 	mov	sp,  x10				\n\
// 	"						\
// 	LOAD_FPU(x11, x12, x1)				\
// 	"ret\n"						);




#ifdef SETJMP_NORMAL_RESUME
	typedef cresume_ctx_t		fresume_t;
	#define SETJMP(jmpbuf)		__ssetjmp(jmpbuf)
	#define LONGJMP(jmpbuf, val)	do{ __slongjmp(jmpbuf, (word_t)val); }while(0)
#else
	/* 使用builtin_setjmp和longjmp */
	#define SETJMP(jmpbuf)		\
	({ fresume_t *__tmpres; SASSERT(IS_SAMETYPE(__tmpres, (jmpbuf))); \
		__builtin_setjmp((void **)(jmpbuf));})
	/* val只能传1 */
	#define LONGJMP(jmpbuf, val)	\
	{ SASSERT((val) == 1); fresume_t *__tmpres; \
		SASSERT(IS_SAMETYPE(__tmpres, (jmpbuf))); \
		__builtin_longjmp((void **)jmpbuf, (word_t)val); UNREACHABLE(); }

	typedef struct fresume_buf{
		word_t fp;
		word_t pc;
		word_t sp;
	} fresume_t;
#endif

static inline void *get_fresume_sp(fresume_t *t){
	return (void *)t->sp;
}

/* 完整的需要设置一堆确保能unwind, 参考https://github.com/koka-lang/libmprompt */

/* 不管unwind, 直接简单粗暴, 最大化性能 */
#define __setstack_exec(func, arg, sp)			\
asm volatile("						\n\
	mov	sp, %0					\n\
	mov	x0, %1					\n\
	mov	x1, %2					\n\
	br	x1"					\
	:						\
	: "r"(sp), "r"(arg), "r"(func)		\
	: "x0", "x1" /*, "x30", "memory"*/  /*,"cc"*/	\
	);						\
UNREACHABLE()

#define __setstack_exec2(func, arg0, arg1, sp)		\
asm volatile("						\n\
	mov	sp, %0					\n\
	mov	x0, %1					\n\
	mov	x1, %2					\n\
	mov	x2, %3					\n\
	br	x2					\n"\
	:						\
	: "r"(sp), "r"(arg0), "r"(arg1), "r"(func)	\
	: "x0", "x1", "x2", "memory" /*,"cc"*/);	\
UNREACHABLE()

/* 在编译器已知函数func的情况下，避免indirect call(依赖访存结果的call)，防止CPU流水线阻塞  */
#define __setstack_exec2_const(func, arg0, arg1, sp)	\
asm volatile("						\n\
	mov	sp, %0					\n\
	mov	x0, %1					\n\
	mov	x1, %2					\n\
	b	%3					\n"\
	:						\
	: "r"(sp), "r"(arg0), "r"(arg1), "i"(func)	\
	: "x0", "x1" /*,"memory"*//* don't need */);	\
UNREACHABLE()

#define setstack_exec2(func, arg0, arg1, sp)	do{				\
	if(CAN_EVAL(func)){							\
		__setstack_exec2_const(func, (word_t)arg0, (word_t)arg1, sp);	\
	}else{									\
		__setstack_exec2(func, (word_t)arg0, (word_t)arg1, sp);		\
	}									\
}while(0)

#define setstack_exec(func, arg, sp)		do{ __setstack_exec(func, (word_t)arg, sp); }while(0)
